4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
15 // BUILD.C -- build routines
18 // Module contains routines to build targets
25 // In order to make comparing dates easier, we cast the FILEINFO buffer to
26 // be of type BOGUS, which has one long where the two unsigneds (for date
27 // and time) are in the original buffer. That way only need a single compare.
30 // function prototypes for the module
31 // I make as many things static as possible, just to be extra cautious
34 int build(MAKEOBJECT
*, UCHAR
, time_t *, BOOL
, char *, BATCHLIST
**);
36 MAKEOBJECT
* makeTempObject(char*, UCHAR
);
37 void insertSort(DEPLIST
**pDepList
, DEPLIST
*pElement
);
38 BOOL
nextToken(char**, char**);
39 DEPLIST
* createDepList(BUILDBLOCK
*pBlock
, char *objectName
);
40 void addBatch(BATCHLIST
**pBatchList
, RULELIST
*pRule
,
41 MAKEOBJECT
*pObject
, char *dollarLt
);
42 int doBatchCommand (BATCHLIST
*pBatch
);
43 int RecLevel
= 0; // Static recursion level. Changed from function
44 // parameter because of treatment of recursive makes.
45 int execBatchList(BATCHLIST
*);
46 void freeBatchList(BATCHLIST
**);
47 int invokeBuildEx(char *, UCHAR
, time_t *, char *, BATCHLIST
**);
49 // we have to check for expansion on targets -- firstTarget had to be
50 // expanded earlier to tell whether or not we were dealing w/ a rule, etc.,
51 // but targets from commandline might have macros, wildcards in them
58 NMHANDLE searchHandle
;
62 for (p
= makeTargets
; p
; p
= makeTargets
) {
63 if (_tcspbrk(makeTargets
->text
, "*?")) { // expand wildcards
64 struct _finddata_t finddata
;
67 if ((szFilename
= findFirst(makeTargets
->text
, &finddata
, &searchHandle
))) {
69 v
= prependPath(makeTargets
->text
, szFilename
);
70 dateTime
= getDateTime(&finddata
);
71 status
= invokeBuild(v
, flags
, &dateTime
, NULL
);
73 if ((status
< 0) && (ON(gFlags
, F1_QUESTION_STATUS
))) {
74 freeStringList(p
); // Was not being freed
77 } while ((szFilename
= findNext(&finddata
, searchHandle
)));
79 makeError(0, NO_WILDCARD_MATCH
, makeTargets
->text
);
83 status
= invokeBuild(makeTargets
->text
, flags
, &dateTime
, NULL
);
84 if ((status
< 0) && (ON(gFlags
, F1_QUESTION_STATUS
))) {
85 freeStringList(p
); // Was not being freed
89 makeTargets
= p
->next
;
103 BATCHLIST
*pLocalBatchList
= NULL
;
104 status
+= invokeBuildEx(target
,
110 if (pLocalBatchList
) {
111 status
+= execBatchList (pLocalBatchList
);
112 freeBatchList (&pLocalBatchList
);
125 BATCHLIST
**ppBatchList
)
128 BOOL fInmakefile
= TRUE
;
132 if (!(object
= findTarget(target
))) {
133 object
= makeTempObject(target
, pFlags
);
136 rc
= build(object
, pFlags
, timeVal
, fInmakefile
, pFirstDep
, ppBatchList
);
149 BATCHLIST
**ppBatchList
)
151 STRINGLIST
*questionList
,
155 struct _finddata_t finddata
; // buffer for getting file times
158 RULELIST
*rule
; // pointer to rule found to build target
161 DEPLIST
*deps
, *deplist
;
164 time_t targTime
, // target's time in file system
165 newTargTime
, // target's time after being rebuilt
167 depTime
, // time of dependency just built
168 maxDepTime
; // time of most recent dependency built
169 BOOL built
; // flag: target built with doublecolon commands
170 time_t *blockTime
; // points to dateTime of cmd. block
171 extern UCHAR okToDelete
;
173 BATCHLIST
*pLocalBatchList
;
177 printf("Build '%s'\n", object
->name
);
180 // The first dependent or inbuilt rule dependent is reqd for extmake syntax
181 // handling. If it has a value then it is the dependent corr to the inf rule
182 // otherwise it should be the first dependent specified
189 if (ON(object
->flags3
, F3_BUILDING_THIS_ONE
)) // detect cycles
190 makeError(0, CYCLE_IN_TREE
, object
->name
);
192 if (object
->ppBatch
) {
193 // we need to build an object that is already placed in a batch list
194 // Go ahead and build the whole batch list
195 BATCHLIST
**ppBatch
= object
->ppBatch
;
196 status
+= execBatchList (*ppBatch
);
197 freeBatchList(ppBatch
);
198 *targetTime
= object
->dateTime
;
202 if (ON(object
->flags3
, F3_ALREADY_BUILT
)) {
203 if (ON(parentFlags
, F2_DISPLAY_FILE_DATES
))
204 printDate(RecLevel
*2, object
->name
, object
->dateTime
);
205 *targetTime
= object
->dateTime
;
206 if ( OFF(gFlags
, F1_QUESTION_STATUS
) &&
208 OFF(object
->flags3
, F3_OUT_OF_DATE
) &&
209 findFirst(object
->name
, &finddata
, &tHandle
)) {
210 // Display 'up-to-date' msg for built level-1 targets
211 // that exist as files.
212 makeMessage(TARGET_UP_TO_DATE
, object
->name
);
214 return(ON(object
->flags3
, F3_OUT_OF_DATE
)? 1 : 0);
228 pLocalBatchList
= NULL
;
231 SET(object
->flags3
, F3_BUILDING_THIS_ONE
);
232 dollarStar
= dollarAt
= object
->name
;
234 // For Double Colon case we need the date of target before it's target's are
235 // built. For all other cases the date matters only if dependents are up
236 // to date. NOT TRUE: WE ALSO NEED THE TARGET'S TIME for @?
238 b
= object
->buildList
;
239 if (b
&& ON(b
->buildBlock
->flags
, F2_DOUBLECOLON
)
240 && findFirst(object
->name
, &finddata
, &tHandle
)) {
241 targTime
= getDateTime(&finddata
);
245 for (; b
; b
= b
->next
) {
247 block
= b
->buildBlock
;
248 if (block
->dateTime
!= 0) { // cmd. block already executed
249 targTime
= __max(targTime
, block
->dateTime
);
251 continue; // so set targTime and skip this block
253 blockTime
= &block
->dateTime
;
255 deplist
= deps
= createDepList(block
, object
->name
);
256 for (;deps
; deps
= deps
->next
) {
257 tempTime
= deps
->depTime
;
258 rc
= invokeBuildEx(deps
->name
, // build the dependent
260 &tempTime
, NULL
, &pLocalBatchList
);
262 if (fOptionK
&& rc
) {
263 MAKEOBJECT
*obj
= findTarget(deps
->name
);
265 if (OFF(obj
->flags3
, F3_ERROR_IN_CHILD
)) {
266 fSlashKStatus
= FALSE
;
267 makeError(0, BUILD_FAILED_SLASH_K
, deps
->name
);
269 SET(object
->flags3
, F3_ERROR_IN_CHILD
);
271 depTime
= __max(depTime
, tempTime
);/*if rebuilt, change time*/
273 // If target exists then we need it's timestamp to correctly construct $?
275 if (!targTime
&& OFF(block
->flags
, F2_DOUBLECOLON
) &&
276 findFirst(object
->name
, &finddata
, &tHandle
)) {
277 object
->dateTime
= targTime
= getDateTime(&finddata
);
280 // If dependent was rebuilt, add to $?. [RB]
282 if (ON(object
->flags2
, F2_FORCE_BUILD
) ||
283 targTime
< tempTime
||
284 (fRebuildOnTie
&& targTime
== tempTime
)
286 temp
= makeNewStrListElement();
287 temp
->text
= makeString(deps
->name
);
288 appendItem(&questionList
, temp
);
291 // Always add dependent to $**. Must allocate new item because two
292 // separate lists. [RB]
294 temp
= makeNewStrListElement();
295 temp
->text
= makeString(deps
->name
);
296 appendItem(&starList
, temp
);
299 if (pLocalBatchList
) {
300 // Perform deferred batch builds and free batch list
301 status
+= execBatchList (pLocalBatchList
);
302 freeBatchList(&pLocalBatchList
);
305 // Free dependent list
307 for (deps
= deplist
; deps
; deps
= deplist
) {
309 deplist
= deps
->next
;
313 // Now, all dependents are built.
315 if (ON(block
->flags
, F2_DOUBLECOLON
)) {
317 // do doublecolon commands
319 if (block
->buildCommands
) {
320 dollarQuestion
= questionList
;
321 dollarStar
= dollarAt
= object
->name
;
322 dollarLessThan
= dollarDollarAt
= NULL
;
323 dollarStarStar
= starList
;
324 if (((fOptionK
&& OFF(object
->flags3
, F3_ERROR_IN_CHILD
)) ||
326 (targTime
< depTime
) ||
327 (fRebuildOnTie
&& (targTime
== depTime
)) ||
328 (targTime
== 0 && depTime
== 0) ||
332 // do commands if necessary
337 // if the first dependent is not set use the first one
338 // from the list of dependents
340 pFirstDep
= pFirstDep
? pFirstDep
: (dollarStarStar
?
341 dollarStarStar
->text
: NULL
);
342 status
+= doCommands(object
->name
,
343 block
->buildCommands
,
348 if (OFF(object
->flags2
, F2_NO_EXECUTE
) &&
349 findFirst(object
->name
, &finddata
, &tHandle
))
350 newTargTime
= getDateTime(&finddata
);
352 newTargTime
= maxDepTime
;
354 curTime(&newTargTime
); // currentTime
356 // set time for this block
357 block
->dateTime
= newTargTime
;
360 // If these both point to the same list,
363 if (starList
!= questionList
) {
364 freeStringList(starList
);
365 freeStringList(questionList
);
367 freeStringList(starList
);
370 starList
= questionList
= NULL
;
374 if (fOptionK
&& ON(object
->flags3
, F3_ERROR_IN_CHILD
))
375 makeError(0, TARGET_ERROR_IN_CHILD
, object
->name
);
379 // singlecolon; set explComBlock
381 if (block
->buildCommands
)
383 makeError(0, TOO_MANY_RULES
, object
->name
);
385 explComBlock
= block
;
386 maxDepTime
= __max(maxDepTime
, depTime
);
389 if (ON(block
->flags
, F2_DOUBLECOLON
) && !b
->next
) {
390 CLEAR(object
->flags3
, F3_BUILDING_THIS_ONE
);
391 SET(object
->flags3
, F3_ALREADY_BUILT
);
393 SET(object
->flags3
, F3_OUT_OF_DATE
);
395 CLEAR(object
->flags3
, F3_OUT_OF_DATE
);
396 targTime
= __max(newTargTime
, targTime
);
397 object
->dateTime
= targTime
;
398 *targetTime
= targTime
;
403 dollarLessThan
= dollarDollarAt
= NULL
;
405 if (!(targTime
= *targetTime
)) {
406 if (object
->dateTime
) {
407 targTime
= object
->dateTime
;
408 } else if (findFirst(object
->name
, &finddata
, &tHandle
)) {
409 targTime
= getDateTime(&finddata
);
413 if (ON(object
->flags2
, F2_DISPLAY_FILE_DATES
)) {
414 printDate(RecLevel
*2, object
->name
, targTime
);
419 // look for implicit dependents and use rules to build the target
421 // The order of the if's decides whether the dependent is inferred
422 // from the inference rule or not, even when the explicit command block is
423 // present, currently it is infered (XENIX MAKE compatibility)
425 if ((rule
= useRule(object
,
435 dollarLessThan
= name
;
436 implComList
= rule
->buildCommands
;
440 dollarStar
= dollarAt
= object
->name
;
441 dollarQuestion
= questionList
;
442 dollarStarStar
= starList
;
444 if (((fOptionK
&& OFF(object
->flags3
, F3_ERROR_IN_CHILD
)) || status
== 0) &&
445 (targTime
< maxDepTime
||
446 (fRebuildOnTie
&& (targTime
== maxDepTime
)) ||
447 (targTime
== 0 && maxDepTime
== 0) ||
448 ON(object
->flags2
, F2_FORCE_BUILD
)
451 okDel
= okToDelete
; // Yes, can delete while executing commands
455 // if the first dependent is not set use the first one from the
456 // list of dependents
457 pFirstDep
= pFirstDep
? pFirstDep
:
458 (dollarStarStar
? dollarStarStar
->text
: NULL
);
459 status
+= doCommands(object
->name
, // do singlecolon commands
460 explComBlock
->buildCommands
,
461 explComBlock
->buildMacros
,
465 else if (implComList
) {
466 if (rule
->fBatch
&& OFF(gFlags
, F1_NO_BATCH
)) {
467 addBatch(ppBatchList
,
473 status
+= doCommands(object
->name
, // do rule's commands
480 else if (ON(gFlags
, F1_TOUCH_TARGETS
)) { // for /t with no commands...
482 status
+= doCommands(object
->name
,
483 block
->buildCommands
,
488 // if Option K specified don't exit ... pass on return code
489 else if (!fInmakefile
&& targTime
== 0) { // lose
490 // If option K, then set the return code 'status'
491 // to 1 to indicate a failure.
495 makeError(0, CANT_MAKE_TARGET
, object
->name
);
498 // if cmd exec'ed or has 0 deps then currentTime else max of dep times
499 if (explComBlock
|| implComList
|| !dollarStarStar
) {
500 curTime(&newTargTime
);
502 // Add 2 to ensure the time for this node is >= the time the file
503 // system might have used (mainly useful when running a very fast
504 // machine where the file system doesn't have the resolution of the
505 // system timer... We don't have to to this in the curTime
506 // above since it's only hit when nothing is built anywhere...
510 newTargTime
= maxDepTime
;
512 if (blockTime
&& explComBlock
)
513 // set block's time, if a real cmd. block was executed
514 *blockTime
= newTargTime
;
516 else if (OFF(gFlags
, F1_QUESTION_STATUS
) &&
519 OFF(object
->flags3
, F3_ERROR_IN_CHILD
))
520 makeMessage(TARGET_UP_TO_DATE
, object
->name
);
522 if (fOptionK
&& status
) {
523 // We should set fSlashKStatus=FALSE so that main() knows the
524 // build failed under /K option.
525 fSlashKStatus
= FALSE
;
527 if (ON(object
->flags3
, F3_ERROR_IN_CHILD
))
528 makeError(0, TARGET_ERROR_IN_CHILD
, object
->name
);
529 else if (RecLevel
== 1)
530 makeError(0, BUILD_FAILED_SLASH_K
, object
->name
);
533 if (ON(gFlags
, F1_QUESTION_STATUS
) && RecLevel
== 1 ) {
534 // If these both point to the same list, don't free twice.
536 if (starList
!= questionList
) {
537 freeStringList(starList
);
538 freeStringList(questionList
);
540 freeStringList(starList
);
543 return(numCommands
? -1 : 0);
546 CLEAR(object
->flags3
, F3_BUILDING_THIS_ONE
);
547 if (!object
->ppBatch
) {
548 SET(object
->flags3
, F3_ALREADY_BUILT
);
550 SET(object
->flags3
, F3_OUT_OF_DATE
);
552 CLEAR(object
->flags3
, F3_OUT_OF_DATE
);
555 targTime
= __max(newTargTime
, targTime
);
556 object
->dateTime
= targTime
;
558 *targetTime
= targTime
;
560 // If these both point to the same list, don't free twice.
562 if (starList
!= questionList
) {
563 freeStringList(starList
);
564 freeStringList(questionList
);
566 freeStringList(starList
);
578 BOOL again
; // flag: wildcards found in dependent name
580 char *source
, *save
, *token
;
581 char *depName
, *depPath
;
583 STRINGLIST
*sList
, *pMacros
;
584 DEPLIST
*depList
= NULL
, *pNew
;
585 struct _finddata_t finddata
;
586 NMHANDLE searchHandle
;
588 pMacros
= bBlock
->dependentMacros
;
590 // expand Macros in Dependent list
591 for (sList
= bBlock
->dependents
; sList
; sList
= sList
->next
) {
592 for (s
= sList
->text
; *s
&& *s
!= '$'; s
= _tcsinc(s
)) {
597 // set $$@ properly, The dependency macros will then expand right
598 dollarDollarAt
= objectName
;
599 source
= expandMacros(sList
->text
, &pMacros
);
601 source
= sList
->text
;
603 save
= makeString(source
);
604 // build list for all dependents
605 for (t
= save
; nextToken(&t
, &token
);) {
608 for (depName
= token
; *depName
&& *depName
!= '}'; depName
= _tcsinc(depName
)) {
609 if (*depName
== ESCH
) {
619 depName
= token
; // If no path list, set
620 token
= NULL
; // token to null.
623 // depName is now name of dependency file ...
626 putDateTime(&finddata
, 0L);
627 depPath
= makeString(depName
);
628 if (_tcspbrk(depName
, "*?") || token
) { // do wildcards in filename
629 if ((tempStr
= searchPath(token
, depName
, &finddata
, &searchHandle
))){
632 depName
= tempStr
; // depName gets actual name
633 depPath
= prependPath(depName
, getFileName(&finddata
));
634 } // depPath gets full path
637 // Add to the dependent list
640 pNew
= MakeNewDepListElement();
641 // if name contains spaces and has no quotes,
642 // add enclosing quotes around it
643 if (_tcschr(depPath
, ' ') && !_tcschr(depPath
, '\"')) {
644 pNew
->name
= (char *)rallocate (_tcslen(depPath
)+3);
645 *(pNew
->name
) = '\"';
646 *(pNew
->name
+1) = '\0';
647 _tcscat (pNew
->name
, depPath
);
648 _tcscat (pNew
->name
, "\"");
651 pNew
->name
= makeString(depPath
);
654 if (!fDescRebuildOrder
|| findFirst(depPath
, &finddata
, &searchHandle
)) {
655 pNew
->depTime
= getDateTime(&finddata
);
660 if (fDescRebuildOrder
) {
661 insertSort(&depList
, pNew
);
663 appendItem((STRINGLIST
**)&depList
, (STRINGLIST
*)pNew
);
667 _tcspbrk(depName
, "*?") && // do all wildcards
668 findNext(&finddata
, searchHandle
) &&
669 (depPath
= prependPath(depName
, getFileName(&finddata
)))
672 // One dependent (w/wildcards?) was expanded
674 if (source
!= sList
->text
) {
681 // Now, all dependents are done ...
693 DEPLIST
*pList
, *current
;
695 item
= pElement
->depTime
;
696 pList
= current
= *pDepList
;
698 for (;pList
&& item
<= pList
->depTime
; pList
= pList
->next
) {
702 if (current
== pList
) {
703 *pDepList
= pElement
;
705 current
->next
= pElement
;
706 pElement
->next
= pList
;
719 while (*s
&& WHITESPACE(*s
)) {
723 if (!*(*pToken
= s
)) {
731 while (*s
&& *++s
!= '"')
735 // lexer possible internal error: missed a quote
736 makeError(0, LEXER_INTERNAL
);
745 } else if (*s
== '{') {
746 // skip to '}' outside quotes
750 s
++; // Skip the first quote
751 while (*s
&& *s
++ != '"'); // Skip all including the last quote
759 // lexer possible internal error: missed a brace
760 makeError(0, MISSING_CLOSING_BRACE
);
764 while (*s
&& *++s
!= '"')
768 // lexer possible internal error: missed a quote
769 makeError(0, LEXER_INTERNAL
);
781 while (*s
&& !WHITESPACE(*s
)) {
802 while ((temp
= list
)) {
805 FREE_STRINGLIST(temp
);
810 // makeTempObject -- make an object to represent implied dependents
812 // We add implied dependents to the target table, but use a special struct
813 // that has no pointer to a build list -- they never get removed.
814 // time-space trade-off -- can remove them, but will take more proc time.
825 object
= makeNewObject();
826 object
->name
= makeString(target
);
827 object
->flags2
= flags
;
829 object
->dateTime
= 0L;
830 object
->buildList
= NULL
;
831 i
= hash(target
, MAXTARGET
, (BOOL
) TRUE
);
832 prependItem((STRINGLIST
**)targetTable
+i
, (STRINGLIST
*)object
);
839 BATCHLIST
**ppBatchList
,
847 BATCHLIST
*pBatchPrev
= 0;
849 for(pBatch
= *ppBatchList
; pBatch
; pBatch
= pBatch
->next
) {
850 if (pBatch
->pRule
== pRule
&&
851 pBatch
->flags
== pObject
->flags2
)
856 pBatch
= makeNewBatchListElement();
857 pBatch
->pRule
= pRule
;
858 pBatch
->flags
= pObject
->flags2
;
860 pBatchPrev
->next
= pBatch
;
862 else if(*ppBatchList
) {
863 (*ppBatchList
)->next
= pBatch
;
866 *ppBatchList
= pBatch
;
869 temp
= makeNewStrListElement();
870 temp
->text
= makeString(pObject
->name
);
871 appendItem(&pBatch
->nameList
, temp
);
873 temp
= makeNewStrListElement();
874 temp
->text
= makeString(dollarLessThan
);
875 appendItem(&pBatch
->dollarLt
, temp
);
877 assert(!pObject
->ppBatch
);
878 pObject
->ppBatch
= ppBatchList
;
889 STRINGLIST
*pStrList
;
890 RULELIST
*pRule
= pBatch
->pRule
;
891 assert (pBatch
->dollarLt
);
892 assert (pBatch
->nameList
);
895 for (pStrList
= pBatch
->dollarLt
; pStrList
; pStrList
= pStrList
->next
) {
896 cbStr
+= _tcslen(pStrList
->text
) + 1;
897 // allow space for quotes if text contains spaces
898 if (_tcschr(pStrList
->text
, ' '))
901 pchBuf
= (char *)allocate(cbStr
+ 1);
903 for (pStrList
= pBatch
->dollarLt
; pStrList
; pStrList
= pStrList
->next
) {
905 // Quote only if not quoted and contains spaces [vs98:8677]
906 fQuote
= pStrList
->text
[0] != '"' && _tcschr(pStrList
->text
, ' ');
908 _tcscat(pchBuf
, "\"");
909 _tcscat(pchBuf
, pStrList
->text
);
910 _tcscat(pchBuf
, fQuote
? "\" " : " ");
912 dollarLessThan
= pchBuf
;
914 rc
= doCommandsEx(pBatch
->nameList
,
915 pRule
->buildCommands
,
923 for (pName
= pBatch
->nameList
; pName
; pName
= pName
->next
) {
924 pObject
= findTarget(pName
->text
);
927 SET(pObject
->flags3
, F3_ALREADY_BUILT
);
928 CLEAR(pObject
->flags3
, F3_OUT_OF_DATE
);
930 pObject
->ppBatch
= 0;
949 for (pBatch
= pBList
; pBatch
; pBatch
=pBatch
->next
) {
950 status
+= doBatchCommand (pBatch
);
962 BATCHLIST
*pBatch
= *ppBList
;
966 free_stringlist(pBatch
->nameList
);
967 free_stringlist(pBatch
->dollarLt
);
969 pBatch
= pBatch
->next
;